import json
import numpy as np
import math
from scipy.ndimage import distance_transform_edt
from collections import Counter

def read_json(filename):
    with open(filename, 'r') as file:
        data = json.load(file)
    return data

def euler_to_rotation_matrix(euler):
    rx, ry, rz = euler
    Rx = np.array([
        [1, 0, 0],
        [0, math.cos(rx), -math.sin(rx)],
        [0, math.sin(rx), math.cos(rx)]
    ])
    Ry = np.array([
        [math.cos(ry), 0, math.sin(ry)],
        [0, 1, 0],
        [-math.sin(ry), 0, math.cos(ry)]
    ])
    Rz = np.array([
        [math.cos(rz), -math.sin(rz), 0],
        [math.sin(rz), math.cos(rz), 0],
        [0, 0, 1]
    ])
    R = Rz @ Ry @ Rx
    return R

def build_camera_matrix(location, rotation_euler):
    R = euler_to_rotation_matrix(rotation_euler)
    T = np.array(location).reshape(3,1)
    M = np.eye(4)
    M[:3, :3] = R
    M[:3, 3] = T.flatten()
    return M

def world_to_camera(loc_world, cam_location, cam_rotation):
    M = build_camera_matrix(cam_location, cam_rotation)
    M_inv = np.linalg.inv(M)
    point_world = np.array([loc_world[0], loc_world[1], loc_world[2], 1.0])
    point_cam = M_inv @ point_world
    return point_cam[:3]  # 返回 x, y, z

def project_point(point_cam, fov, image_width, image_height):
    x, y, z = point_cam
    if z <= 0:
        return None
    fov_x, fov_y = fov
    fx = image_width / (2 * math.tan(fov_x/2))
    fy = image_height / (2 * math.tan(fov_y/2))
    cx = image_width / 2.0
    cy = image_height / 2.0
    u = fx * (x / z) + cx
    v = fy * (y / z) + cy
    if u < 0 or u >= image_width or v < 0 or v >= image_height:
        return None
    return (u, v)

def world_to_camera_pixel(loc_world, cam_info, image_width = 960, image_height = 540): # WIDTH, HEIGHT
    cam_location = cam_info.get("location")
    cam_rotation = cam_info.get("rotation")
    fov = cam_info.get("fov")
    
    point_cam = world_to_camera(loc_world, cam_location, cam_rotation)
    
    point_cam = -point_cam

    pixel = project_point(point_cam, fov, image_width, image_height)
    return pixel

def mask_decoder(rle):
    counts = rle.get("counts", [])
    size = rle.get("size", [])

    height, width = size
    total_pixels = height * width

    flat_mask = np.zeros(total_pixels, dtype=np.uint8)

    current_index = 0
    current_val = rle.get("prefix", 0)
    for count in counts:
        if current_index + count > total_pixels:
            raise ValueError("RLE Error")
        flat_mask[current_index:current_index+count] = current_val
        current_index += count
        current_val = 1 - current_val

    if current_index != total_pixels:
        raise ValueError("RLE Error")

    mask = flat_mask.reshape((height, width))
    return mask

def get_mask_center(mask): # HEIGHT, WIDTH
    dist = distance_transform_edt(mask)
    center = np.unravel_index(np.argmax(dist), mask.shape)
    return center

def get_main_cate(objs):
    categories = [item['cate'] for item in objs]
    category_counts = Counter(categories)
    most_common_category, most_common_count = category_counts.most_common(1)[0]
    if most_common_count > 1:
        return most_common_category, most_common_count
    else:
        return None, None